using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Text;
using System.Threading.Tasks;
using mb;

class ServerProber : API.IServerProber
{
    ILog logger = mb.ServiceLocator.Instance.Get<ILog>();
    public event API.OnNewServer onNewServer;

    List<Socket> probeSockets = new List<Socket>();
    int probeServerPort;

    public void SetPort(int port)
    {
        probeServerPort = port;

    }
    public void Close()
    {
        foreach (var socket in probeSockets)
        {
            socket.Close();
        }
        probeSockets.Clear();
    }

    public void Refresh()
    {
        Close();

        List<IPAddress> allAddress = new List<IPAddress>();
        IPEndPoint toEndPoint = new IPEndPoint(IPAddress.Broadcast, probeServerPort);
        bool isAndroidEmulator = false;
#if __ANDROID__
        isAndroidEmulator = IsAndroidEmulator();
#endif
        if (!isAndroidEmulator)
        {
            NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces(); //get all network interfaces of the computer
            logger.Log("get nics: {0}", nics.Length);
            foreach (NetworkInterface adapter in nics)
            {
                IPInterfaceProperties adapterProperties = adapter.GetIPProperties();
                logger.Log("adapter", adapter.Name, adapter.NetworkInterfaceType);
                var addressList = adapterProperties.UnicastAddresses;
                if (addressList.Count == 0) continue;
                logger.Log("adapter addr", addressList.First().Address);
                foreach (var ua in adapterProperties.UnicastAddresses)
                {
                    allAddress.Add(ua.Address);
                }
            }
        }
        else
        {
            allAddress.Add(IPAddress.Any);
            toEndPoint = new IPEndPoint(IPAddress.Parse("10.0.2.2"), probeServerPort);
        }
        logger.Log("Broadcast address", toEndPoint.ToString());

        foreach (var addr in allAddress)
        {
            try
            {
                IPEndPoint myEndPoint = new IPEndPoint(addr, 45000);
                Socket client = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
                // client.Bind (myEndPoint);
                client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, 1);
                client.SendTo(Encoding.ASCII.GetBytes("Seek"), toEndPoint);

                if (probeServerPort > 10000)
                {
                    var DevPort = new IPEndPoint(toEndPoint.Address, probeServerPort - 10000);
                    client.SendTo(Encoding.ASCII.GetBytes("Seek"), DevPort);
                }
                probeSockets.Add(client);

                logger.Log("BroadCast in: {0}", addr.ToString());
                byte[] buffer = new byte[1000];

                EndPoint epFrom = new IPEndPoint(IPAddress.Any, 0);
                AsyncCallback recv = null;
                // client.BeginReceiveFrom(buffer, 0, 1000, SocketFlags.None, ref epFrom, recv = (ar) =>
                client.BeginReceiveFrom(buffer, 0, 1000, SocketFlags.None, ref epFrom, recv = (ar) =>
                {
                    try
                    {

                        int bytes = client.EndReceiveFrom(ar, ref epFrom);
                        string name = Encoding.ASCII.GetString(buffer, 0, bytes);
                        string serverIp = (epFrom as IPEndPoint).Address.ToString();
                        // logger.Log("udp reply: {0}: [{1}] {2}", epFrom.ToString(), bytes, name, System.Threading.Thread.CurrentThread.ManagedThreadId);
                        client.BeginReceiveFrom(buffer, 0, 1000, SocketFlags.None, ref epFrom, recv, buffer);

                        //需要事件接者去排重
                        this.onNewServer(name, serverIp, (epFrom as IPEndPoint).Port - 1);
                    }
                    catch (System.ObjectDisposedException e)
                    {
                        // logger.Log("socket disposed ", e);
                    }
                    catch (System.Exception e)
                    {
                        logger.Log("probe error", e);
                    }

                }, buffer);
            }
            catch (Exception e)
            {
                logger.Log("BroadCast Fail in: {0},{1}", e, addr);
            }
        }
    }

#if __ANDROID__
    bool IsAndroidEmulator()
    {
        string fingerPrint = Android.OS.Build.Fingerprint;
        return fingerPrint.Contains("generic");
    }
#endif

}